var torchd = {
	gmaps:{}
};

/**
 * jQuery Wrapper
 */

$.fn.gmaps = function(options) {
	
	return this.each(function() {
		// Create Map
		options.element = this;
		new torchd.gmaps.map(options);
		
	});
};

// Add bind if it's not there
if(typeof Function.prototype.bind != 'function') {
	Function.prototype.bind = function (bind) {
	    var self = this;
	    return function () {
	        var args = Array.prototype.slice.call(arguments);
	        return self.apply(bind || null, args);
	    };
	};
}



//////////////////////////////////////////////////

torchd.gmaps.map = function(parameters) {
	this.initMap(parameters);
};

/////////////////////////////////////////////////////////////////////////
// CLASS METHODS
/////////////////////////////////////////////////////////////////////////


/**
 * Initialize the geocoder
 */
torchd.gmaps.map.geocoder = new google.maps.Geocoder();

/**
 * Create a point of latitude and longitude values
 *
 * @param float latitude
 * @param float longitude
 * @return google.maps.LatLng
 */
torchd.gmaps.map.parsePoint = function(latitude, longitude) {
	return new google.maps.LatLng(latitude, longitude);
};

/**
 * Return a map type constant from a string defining the type
 *
 * @param string type
 * @return google.maps.MapTypeID.X
 */
torchd.gmaps.map.parseType = function (type) {
	var gmapType;
	switch(type) { 
		case 'roadmap':
		default:
			gmapType = google.maps.MapTypeId.ROADMAP;
			break;
		case 'satellite':	
			gmapType = google.maps.MapTypeId.SATELLITE;
			break;
		case 'hybrid':
			gmapType = google.maps.MapTypeId.HYBRID;
			break;
		case 'terrain':
			gmapType = google.maps.MapTypeId.TERRAIN;
			break;
	}
	return gmapType;
};

/**
 * Return a zoom control constant from a string defining the style
 *
 * @param string style
 * @return google.maps.ZoomControlStyle.X
 */
torchd.gmaps.map.parseZoomControlStyle = function(style) {
	var styleType;
	switch(style) {
		case 'small':
			styleType = google.maps.ZoomControlStyle.SMALL;
			break;
		case 'large':
			styleType = google.maps.ZoomControlStyle.LARGE;
			break;
		case 'default':
		default:
			styleType = google.maps.ZoomControlStyle.DEFAULT;
			break;
	}
	return styleType;
};

/**
 * Return a control style constant from a string defining the style
 *
 * @param string style
 * @return google.maps.MapTypeControlStyle.X
 */
torchd.gmaps.map.parseTypeControlStyle = function(style) {
	var styleType;
	switch(style) {
		case 'horizontal_bar':
			styleType = google.maps.MapTypeControlStyle.HORIZONTAL_BAR;
			break;
		case 'dropdown_menu':
			styleType = google.maps.MapTypeControlStyle.DROPDOWN_MENU;
			break;
		case 'default':
		default:
			styleType = google.maps.MapTypeControlStyle.DEFAULT;
			break;
	}
	return styleType;
};

/**
 * Return a control position constant from a string defining the position
 *
 * @param string position
 * @return google.maps.ControlPosition.X
 */
torchd.gmaps.map.parseControlPosition = function(position) {
	var positionReturn;
	switch(position) {
		default:
		case 'top_left':
			positionReturn = google.maps.ControlPosition.TOP_LEFT;
			break;
		case 'left_top':
			positionReturn = google.maps.ControlPosition.LEFT_TOP;
			break;
		case 'left_center':
			positionReturn = google.maps.ControlPosition.LEFT_CENTER;
			break;
		case 'left_bottom':
			positionReturn = google.maps.ControlPosition.LEFT_BOTTOM;
			break;
		case 'bottom_left':
			positionReturn = google.maps.ControlPosition.BOTTOM_LEFT;
			break;
		case 'bottom_center':
			positionReturn = google.maps.ControlPosition.BOTTOM_CENTER;
			break;
		case 'bottom_right':
			positionReturn = google.maps.ControlPosition.BOTTOM_RIGHT;
			break;
		case 'right_bottom':
			positionReturn = google.maps.ControlPosition.RIGHT_BOTTOM;
			break;
		case 'right_center':
			positionReturn = google.maps.ControlPosition.RIGHT_CENTER;
			break;
		case 'right_top':
			positionReturn = google.maps.ControlPosition.RIGHT_TOP;
			break;
		case 'top_right':
			positionReturn = google.maps.ControlPosition.TOP_RIGHT;
			break;
		case 'top_center':
			positionReturn = google.maps.ControlPosition.TOP_CENTER;
			break;
	}
	return positionReturn;
};

/**
 * Return a marker animation constant from a string defining the animation
 *
 * @param string animation
 * @return google.maps.Animation.X
 */
torchd.gmaps.map.parseMarkerAnimation = function(animation) {
	var markerAnimation;
	switch(animation) {
		case 'drop':
			markerAnimation = google.maps.Animation.DROP;
			break;
		case 'bounce':
			markerAnimation = google.maps.Animation.BOUNCE;
			break;
		default:
			markerAnimation = false;
			break;
	}
	return markerAnimation;
};

/**
 * Geocode an address
 *
 * @param string address
 * @param function success
 */
torchd.gmaps.map.geocode = function(address, success) {
	torchd.gmaps.map.geocoder.geocode({
		address:address
	},
	function(results, status) {
		success(results, status);
	});
};

/**
 * Default map parameters
 */
torchd.gmaps.map.defaults = {
	center: {
		latitude:'34.43036357495847',
		longitude:'-119.76904392242432',
		address:''
	},
	zoom:4,
	fitToPoints:false,
	geolocation:{
		geolocate:false,
		marker:{
			show:false,
			image:false
		}
	},
	markers:[],
	mapType:'hybrid ',
	controls: {
		pan:{
			show:false,
			position:'top_right'
		},
		zoom:{
			show:true,
			style:'default',
			position:'left_center'
		},
		mapType:{
			show:true,
			style:'default',
			position:'bottom_center'
		},
		scale:{
			show:false,
			position:'top_left'				
		},
		streetView:{
			show:false,
			position:'left_top'
		}
	},
	events:{
		click:false,
		dblclick:false,
		mouseup:false,
		mousedown:false,
		mouseover:false,
		mouseout:false		
	},
	places:{
		search:false,
		center:false,
		radius:false,
		searchBox:false,
		placeChanged:{
			action:false,
			zoom:7
		}
	}
};


/////////////////////////////////////////////////////////////////////////
// INSTANCE METHODS
/////////////////////////////////////////////////////////////////////////
torchd.gmaps.map.prototype = {
	
	initMap: function(params) {
		this.map = false;
		this.bounds = false;
		this.markers = [];
		this.points = [];
		this.geocoder = false;
		this.params = false;
		
		var config = {
			mapTypeId:false,
			zoom:false,
			panControl:false,
			panControlOptions: {
				position:false
			},
			mapTypeControl:false,
			mapTypeControlOptions: {
				position:false,
				style:false
			},
			zoomControl:false,
			zoomControlOptions: {
				position:false,
				style:false
			},
			scaleControl:false,
			scaleControlOptions: {
				position:false
			},
			streetViewControl:false,
			streetViewControlOptions: {
				position:false
			}
		};
		this.params = $.extend({}, torchd.gmaps.map.defaults, params);
		
		/**
		 * Set up the configuration object with the parameters
		 */
		config.mapTypeId = torchd.gmaps.map.parseType(this.params.mapType);
		
		config.zoom = this.params.zoom;
		
		
		
		if(this.params.controls.pan) {
			config.panControl = this.params.controls.pan.show;
			config.panControlOptions.position = torchd.gmaps.map.parseControlPosition(this.params.controls.pan.position);
		}
		if(this.params.controls.mapType) {
			config.mapTypeControl = this.params.controls.mapType.show;
			config.mapTypeControlOptions.position = torchd.gmaps.map.parseControlPosition(this.params.controls.mapType.position);
			config.mapTypeControlOptions.style = torchd.gmaps.map.parseTypeControlStyle(this.params.controls.mapType.style);
		}
		if(this.params.controls.zoom) {
			config.zoomControl = this.params.controls.zoom.show;
			config.zoomControlOptions.position = torchd.gmaps.map.parseControlPosition(this.params.controls.zoom.position);
			config.zoomControlOptions.style = torchd.gmaps.map.parseZoomControlStyle(this.params.controls.zoom.style);
		}
		if(this.params.controls.scale) {
			config.scaleControl = this.params.controls.scale.show;
			config.scaleControlOptions.position = torchd.gmaps.map.parseControlPosition(this.params.controls.scale.position);
		}
		if(this.params.controls.streetView) {
			config.streetViewControl = this.params.controls.streetView.show;
			config.streetViewControlOptions.position = torchd.gmaps.map.parseControlPosition(this.params.controls.streetView.position);
		
		}
		
		
		
		
		
		
		/**
		 * Generate the map
		 */
		this.map = new google.maps.Map(this.params.element, config);
		
		if(this.params.center.address) {
			torchd.gmaps.map.geocode(this.params.center.address, (function(results) {
				this.map.setCenter(results[0].geometry.location);
			}).bind(this));
		}
		else {
			this.map.setCenter(torchd.gmaps.map.parsePoint(this.params.center.latitude, this.params.center.longitude));
		}
		
		
		/**
		 * Add listeners
		 */
		if(typeof(this.params.onClick) == 'function') {
			google.maps.event.addListener(this.map, 'click', (function(event) {
				this.params.onClick(event);
			}).bind(this));
		}
		
		/**
		 * Handle geolocation
		 */
		if(this.params.geolocation.geolocate) {
			this.geolocate(this.params.geolocation);
		}
		
		
		
		/**
		 * Add markers
		 */
		this.parseMarkers(this.params.markers);
		google.maps.event.addListener(this.map, 'bounds_changed', (function() {
        	this.bounds = this.map.getBounds();
        	
      	}).bind(this));
		
		/**
		 * Places search with google places API
		 */
		this.parsePlaces(this.params.places);
		
		
		
		
	},
	addMarker: function(marker) {
		var i = this.markers.length;
		this.initMarker(marker, i);
	},
	initMarker: function(marker, i) {
		this.markers[i] = new google.maps.Marker(this.createMarker(marker));
		this.markers[i].markerNum = i;
		this.markers[i].position = marker.position;
		this.markers[i].onClick = marker.onClick;
		this.markers[i].onMouseOver = marker.onMouseOver;
		this.markers[i].onMouseOut = marker.onMouseOut;
		this.markers[i].onDoubleClick = marker.onDoubleClick;
		this.markers[i].onMouseUp = marker.onMouseUp;
		this.markers[i].onMouseDown = marker.onMouseDown;

		this.markers[i].map = this.map;
		
		/**
		 * Info window
		 */
		if(marker.info) {
			this.markers[i].infoWindow = new google.maps.InfoWindow({
				content:marker.info.html
			});
			google.maps.event.addListener(this.markers[i], 'click', function() {
				this.infoWindow.open(this.map, this);
				
			});
			if(marker.info.autoOpen == true) {
				this.markers[i].infoWindow.open(this.map, this.markers[i]);
			}
		}
		
		/**
		 * Marker listeners
		 */
		if(typeof(this.markers[i].onMouseOver) == 'function') {
			google.maps.event.addListener(this.markers[i], 'mouseover', function() {
				this.onMouseOver();
			});
		}
		if(typeof(this.markers[i].onMouseOut) == 'function') {
			google.maps.event.addListener(this.markers[i], 'mouseout', function() {
				this.onMouseOut();
			});
		}
		if(typeof(this.markers[i].onClick) == 'function') {
			google.maps.event.addListener(this.markers[i], 'click', function() {
				this.onClick();
			});
		}
		if(typeof(this.markers[i].onDoubleClick) == 'function') {
			google.maps.event.addListener(this.markers[i], 'dblclick', function() {
				this.onDoubleClick();
			});
		}
		if(typeof(this.markers[i].onMouseUp) == 'function') {
			google.maps.event.addListener(this.markers[i], 'mouseup', function() {
				this.onMouseUp();
			});
		}
		if(typeof(this.markers[i].onMouseDown) == 'function') {
			google.maps.event.addListener(this.markers[i], 'mousedown', function() {
				this.onMouseDown();
			});
		}
		
		if(this.params.fitToPoints) {
			this.latlngbounds = new google.maps.LatLngBounds( );
			for ( var i = 0; i < this.points.length; i++ ) {
  				this.latlngbounds.extend( this.points[ i ] );
			}
			this.map.fitBounds( this.latlngbounds );
		}
	},
	geocodeMarker: function(marker, i) {
		var self = this;
		torchd.gmaps.map.geocoder.geocode({
				address:marker.address
		
			},
			function(results, status) {
				self.points.push(results[0].geometry.location);
				marker.position = results[0].geometry.location;
				self.initMarker(marker, i);
				
				if(self.params.onGeocode) {
					self.params.onGeocode(results[0].geometry.location);
				}
			}
		);
	},
	
	parseMarkers: function(markers, callback) {
		for(var i=0;i<markers.length;i++) {
		
			// Create the marker and set its listener functions to avoid namespace confusion
			if(markers[i].latitude && markers[i].longitude) {
				var position = torchd.gmaps.map.parsePoint(markers[i].latitude, markers[i].longitude);
				markers[i].position = position;
				this.points.push(position);
				this.initMarker(markers[i], i);
			}
			else if(markers[i].address) {
				// Geocode
				this.geocodeMarker(markers[i], i);
				
			}
			
			
			
		}
		if(callback) {
			callback();
		}
		
	},
	createMarker: function(marker) {
		var gmapMarker = {
			map: this.map,
			position:marker.position,
			draggable:marker.draggable,	
			icon:marker.image
		};
		gmapMarker.animation = torchd.gmaps.map.parseMarkerAnimation(marker.animation);
		
		return gmapMarker;
	},
	
	geolocate: function(params) {
		var self = this;
		if(navigator.geolocation) {
    		browserSupportFlag = true;
    		navigator.geolocation.getCurrentPosition((function(position) {
    			var position = torchd.gmaps.map.parsePoint(position.coords.latitude, position.coords.longitude);
    			this.map.setCenter(position);
    			if(params.marker) {
	    			if(params.marker.show) {
	    				var marker = new google.maps.Marker({
							map: this.map,
							position:position
						});
						if(params.marker.image) {
							marker.icon = params.marker.image;
						}
	    				this.markers.push(marker);
						this.points.push(position);
	    			}
	    		}
    		}).bind(this), function() {
    			alert('Geolocation error');
    		});
       		
 	 	} 
 	  	
      	else {
      		alert('Geolocation error');
      	}
      		
	},
	
	parsePlaces: function(places) {
		if(places.searchBox) {
			var autocompleteOptions = {
				types:['establishment']	
			}
			var autocomplete = new google.maps.places.Autocomplete(document.getElementById(places.searchBox), autocompleteOptions);
			autocomplete.bindTo('bounds', this.map);
			switch(places.placeChanged.action) {
				case 'marker':
					google.maps.event.addListener(autocomplete, 'place_changed', (function() {
						this.clearOverlays();
						var place = autocomplete.getPlace();
						if (place.geometry.viewport) {
							this.map.fitBounds(place.geometry.viewport);
						} 
						else {
						    this.map.setCenter(place.geometry.location);
						  	this.map.setZoom(places.placeChanged.zoom);
						}
						var length = this.markers.length;
						this.markers[length] = new google.maps.Marker({
							map: this.map,
							position:place.geometry.location
						});
						this.markers[length].infoWindow = new google.maps.InfoWindow({
							content:place.name
						});
						this.markers[length].infoWindow.open(this.map, this.markers[length]);
						if(places.placeChanged.callback) {
							places.placeChanged.callback(place);
						}
					}).bind(this));
					break;
			}
		}
	},
	clearOverlays: function() {
		for(var i=0; i < this.markers.length; i++) {
        	this.markers[i].setMap(null);
    	}
    	this.markers = new Array();
	}

}